﻿#-------------------------------------------------------------------------------
# Name:        Towers module. Any tower as located here
# Purpose:
#
# Author:      Lorien
#
# Created:     31.08.2011
# Copyright:   (c) Lorien 2011
# Licence:     Private
#-------------------------------------------------------------------------------
#!/usr/bin/env python

from math import sqrt
from consts import *
from bullets import *
from gobject import GObject


#Towers base-class
class TowerBase(GObject):
    #Game-space tower w and h
    width = 2
    height = 2

    def __init__(self, position, map_worker, game):
        #Initing
        GObject.__init__(self, position, map_worker)
        self.game = game
        self.active = False
        self.build_time = 3  # in seconds; TODO: need override in all derived

        #Initting warfire system
        self.current_time = 0
        self.target = False
        self.target_behavior = False
        self.type_behavior = False
        self.shoot_behavior = False


    def update(self, time_pass):
        if not self.active:
            return

        self.current_time -= time_pass / 1000

        if not self.target:
            self.current_time -= time_pass * 4

        if self.current_time <= 0:
            self.current_time = self.speed

            #release dead target
            if self.target and not self.target.isAlive():
                self.target = None
                #print("Changed target to none as of death")

            #looking for target
            if not self.target:
                self.target = self.target_behavior.getTarget(self)
                if not self.target:
                    pass
                    #print("Target not found")

            #self.shoot_behavior.shoot(self)
            if self.target:
                if self.inRange(self.target):
                    #print("Target is in range")
                    pass
                else:
                    #print("Target is not in range")
                    self.target = None

            self.type_behavior.spawnBullet(self)

    def loadTargetBehavior(self, behavior):
        self.target_behavior = behavior

    def loadShootBehavior(self, behavior):
        self.shoot_behavior = behavior

    def loadTypeBehavior(self, behavior):
        self.type_behavior = behavior
        self.type_behavior.load(self)

    def activate(self):
        #Reloading behavior because of graphics
        self.type_behavior.load(self)

        #Cache values for enemy-searching as we could change range
        self.center = (self.x + self.width // 2, self.y + self.height // 2)
        #x
        self.start_x, self.end_x = int(self.center[0] - self.radius), int(self.center[0] + self.radius)
        if self.start_x < 0: self.start_x = 0
        if self.end_x >= len(self.map_worker.map_objects): self.end_x = len(self.map_worker.map_objects)
        #y
        self.start_y, self.end_y = int(self.center[1] - self.radius), int(self.center[1] + self.radius)
        if self.start_y < 0: self.start_y = 0
        if self.end_y >= len(self.map_worker.map_objects[0]): self.end_y = len(self.map_worker.map_objects[0])

        self.active = True
        ##

    def deactivate(self):
        self.active = False

    def startBuild(self):
        self.loadGraphics('tower_building.png')

    def endBuild(self):
        self.activate()

    def inRange(self, target):
        x, y = self.x, self.y
        tx, ty = target.x, target.y
        dx, dy = tx - x, ty - y #got direction vector
        distance = sqrt(dx * dx + dy * dy)
        if distance <= self.radius: return True
        return False

    def upgrade(self):
        self.type_behavior.upgrade(self)


class TowerFabric:
    def create(id, position, map_worker, game):
        d = {1: TowerMarioMario,
             2: TowerMarioMashroom,
             3: TowerMarioFlower,
             4: TowerMarioBox
        }

        return d[id](position, map_worker, game)


#TargetBehavior - target-searching behavior
class TowerBehaviorTargetSimple():
    def getTarget(self, tower):

        air = True
        ground = True

        if tower.type == AIR:
            air = True
            ground = False
        if tower.type == SPLASH:
            air = False
            ground = True

        minrange = tower.radius + 10
        target = False
        for x in range(tower.start_x, tower.end_x):
            for y in range(tower.start_y, tower.end_y):
                if tower.map_worker.map_objects[x][y]:
                    if tower.map_worker.map_objects[x][y].isMonster() and tower.map_worker.map_objects[x][y].isAlive():

                        if air and not ground and tower.map_worker.map_objects[x][y].type == MONSTERGROUND: continue
                        if ground and not air and tower.map_worker.map_objects[x][y].type == MONSTERAIR: continue

                        dx = tower.map_worker.map_objects[x][y].x - tower.x
                        dy = tower.map_worker.map_objects[x][y].y - tower.y
                        distance = sqrt(dx * dx + dy * dy)
                        if distance < minrange:
                            minrange = distance
                            target = tower.map_worker.map_objects[x][y]
        return target


#We may do another target-searching systems according to different gameplay elements
#such as different checkboxes for towers - only air, most dead monsters, etc.

#TypeBehavior - lvl, damage, speed and etc behavior
class TowerBehaviorTypeMarioMario1():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mario_1.png')

        tower.cost = 5
        tower.upgradecost = 12
        tower.buildtime = 5
        tower.radius = 5
        tower.speed = 1
        tower.damage = 2
        tower.level = 1
        tower.type = DIRECT

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioMario2())


class TowerBehaviorTypeMarioMario2():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mario_2.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 27
        tower.buildtime = 5
        tower.radius = 5
        tower.speed = 0.9
        tower.damage = 5
        tower.level = 2
        tower.type = DIRECT

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioMario3())


class TowerBehaviorTypeMarioMario3():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mario_3.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 61
        tower.buildtime = 5
        tower.radius = 6
        tower.speed = 0.81
        tower.damage = 14
        tower.level = 3
        tower.type = DIRECT

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioMario4())


class TowerBehaviorTypeMarioMario4():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mario_4.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = -1
        tower.buildtime = 5
        tower.radius = 7
        tower.speed = 0.72
        tower.damage = 39
        tower.level = 3
        tower.type = DIRECT

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        pass


class TowerBehaviorTypeMarioMashroom1():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mush_1.png')

        tower.cost = 7
        tower.upgradecost = 16
        tower.buildtime = 5
        tower.radius = 4
        tower.speed = 1.2
        tower.damage = 1.4
        tower.level = 1
        tower.type = SPLASH

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioMashroom2())


class TowerBehaviorTypeMarioMashroom2():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mush_2.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 37
        tower.buildtime = 5
        tower.radius = 4
        tower.speed = 1.2
        tower.damage = 3
        tower.level = 2
        tower.type = SPLASH

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioMashroom3())


class TowerBehaviorTypeMarioMashroom3():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mush_3.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 85
        tower.buildtime = 5
        tower.radius = 5
        tower.speed = 1.2
        tower.damage = 10
        tower.level = 3
        tower.type = SPLASH

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioMashroom4())


class TowerBehaviorTypeMarioMashroom4():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/mush_4.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = -1
        tower.buildtime = 5
        tower.radius = 5
        tower.speed = 1.2
        tower.damage = 27
        tower.level = 3
        tower.type = SPLASH

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        pass


class TowerBehaviorTypeMarioFlower1():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/flower_1.png')

        tower.cost = 5
        tower.upgradecost = 12
        tower.buildtime = 5
        tower.radius = 6
        tower.speed = 0.8
        tower.damage = 3
        tower.level = 1
        tower.type = AIR

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioFlower2())


class TowerBehaviorTypeMarioFlower2():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/flower_2.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 27
        tower.buildtime = 5
        tower.radius = 7
        tower.speed = 0.8
        tower.damage = 8
        tower.level = 2
        tower.type = AIR

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioFlower3())


class TowerBehaviorTypeMarioFlower3():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/flower_3.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 61
        tower.buildtime = 5
        tower.radius = 8
        tower.speed = 0.8
        tower.damage = 21
        tower.level = 3
        tower.type = AIR

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioFlower4())


class TowerBehaviorTypeMarioFlower4():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/flower_4.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = -1
        tower.buildtime = 5
        tower.radius = 9
        tower.speed = 0.8
        tower.damage = 58
        tower.level = 3
        tower.type = AIR

    def spawnBullet(self, tower):
        if tower.target and tower.inRange(tower.target):
            tower.game.bullets.add(BulletSimple(tower.map_worker, tower, tower.target))

    def upgrade(self, tower):
        pass


class TowerBehaviorTypeMarioBox1():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/box_1.png')

        tower.cost = 7
        tower.upgradecost = 16
        tower.buildtime = 5
        tower.radius = 6
        tower.speed = 0.5
        tower.damage = 0
        tower.level = 1
        tower.type = AIR

    def spawnBullet(self, tower):
        pass

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioBox2())


class TowerBehaviorTypeMarioBox2():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/box_2.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 37
        tower.buildtime = 5
        tower.radius = 7
        tower.speed = 0.5
        tower.damage = 0
        tower.level = 2
        tower.type = AIR

    def spawnBullet(self, tower):
        pass

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioBox3())


class TowerBehaviorTypeMarioBox3():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/box_3.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = 85
        tower.buildtime = 5
        tower.radius = 8
        tower.speed = 0.5
        tower.damage = 0
        tower.level = 3
        tower.type = AIR

    def spawnBullet(self, tower):
        pass

    def upgrade(self, tower):
        tower.loadTypeBehavior(TowerBehaviorTypeMarioBox4())


class TowerBehaviorTypeMarioBox4():
    def load(self, tower):
        #Graphics
        tower.loadGraphics('mario/box_4.png')

        tower.cost = tower.cost + tower.upgradecost
        tower.upgradecost = -1
        tower.buildtime = 5
        tower.radius = 9
        tower.speed = 0.5
        tower.damage = 0
        tower.level = 3
        tower.type = AIR

    def spawnBullet(self, tower):
        pass

    def upgrade(self, tower):
        pass


#Shoot behavior - how do we shoot - direct, splash or even another type?
class TowerBehaviorShootDamage():
    def shoot(self, tower, target):
        target.receiveDamage(tower.damage)


class TowerBehaviorShootSplashDamage():
    def shoot(self, tower, target):
        x, y = target.target

        points = []
        if x > 0: points.append((x - 1, y))
        if x < len(tower.map_worker.map_objects) - 1: points.append((x + 1, y))

        if y > 0: points.append((x, y - 1))
        if y < len(tower.map_worker.map_objects[0]) - 1: points.append((x, y + 1))

        if x > 0 and y > 0: points.append((x - 1, y - 1))
        if x < len(tower.map_worker.map_objects) - 1 and y > 0: points.append((x + 1, y - 1))

        if x > 0 and y < len(tower.map_worker.map_objects[0]) - 1: points.append((x - 1, y + 1))
        if x < len(tower.map_worker.map_objects) - 1 and y < len(tower.map_worker.map_objects[0]) - 1: points.append(
            (x + 1, y + 1))

        for p in points:
            if tower.map_worker.map_objects[p[0]][p[1]] and tower.map_worker.map_objects[p[0]][p[1]].isMonster() and \
                            tower.map_worker.map_objects[p[0]][p[1]].type == MONSTERGROUND:
                t = tower.map_worker.map_objects[p[0]][p[1]]
                t.receiveDamage(tower.damage)

        target.receiveDamage(tower.damage)


class TowerBehaviorShootKill():
    #kill on any shoot - a bit cheaty :D
    def shoot(self, tower, target):
        target.kill()


class TowerBehaviorShootSplashKill():
    #kill on any shoot + splash - a huge cheat :D
    def shoot(self, tower, target):
        x, y = target.target

        points = []
        if x > 0: points.append((x - 1, y))
        if x < len(tower.map_worker.map_objects) - 1: points.append((x + 1, y))

        if y > 0: points.append((x, y - 1))
        if y < len(tower.map_worker.map_objects[0]) - 1: points.append((x, y + 1))

        for p in points:
            if tower.map_worker.map_objects[p[0]][p[1]]:
                t = tower.map_worker.map_objects[p[0]][p[1]]
                t.kill()

        target.kill()


#Basic arrow tower - hits single enemy with high damage


#id:1
class TowerMarioMario(TowerBase):
    def __init__(self, position:tuple, map_worker, game):
        #Initing
        TowerBase.__init__(self, position, map_worker, game)

        #Behaviors
        self.loadTargetBehavior(TowerBehaviorTargetSimple())
        self.loadShootBehavior(TowerBehaviorShootDamage())
        self.loadTypeBehavior(TowerBehaviorTypeMarioMario1())


#id:2
class TowerMarioMashroom(TowerBase):
    def __init__(self, position:tuple, map_worker, game):
        #Initing
        TowerBase.__init__(self, position, map_worker, game)

        #Behaviors
        self.loadTargetBehavior(TowerBehaviorTargetSimple())
        self.loadShootBehavior(TowerBehaviorShootSplashDamage())
        self.loadTypeBehavior(TowerBehaviorTypeMarioMashroom1())


#id:3
class TowerMarioFlower(TowerBase):
    def __init__(self, position:tuple, map_worker, game):
        #Initing
        TowerBase.__init__(self, position, map_worker, game)

        #Behaviors
        self.loadTargetBehavior(TowerBehaviorTargetSimple())
        self.loadShootBehavior(TowerBehaviorShootDamage())
        self.loadTypeBehavior(TowerBehaviorTypeMarioFlower1())


#id:4
class TowerMarioBox(TowerBase):
    def __init__(self, position:tuple, map_worker, game):
        #Initing
        TowerBase.__init__(self, position, map_worker, game)

        #Behaviors
        self.loadTargetBehavior(TowerBehaviorTargetSimple())
        self.loadShootBehavior(TowerBehaviorShootDamage())
        self.loadTypeBehavior(TowerBehaviorTypeMarioBox1())

